package business.global.pk.nn;

import java.util.*;
import java.util.stream.Collectors;

import business.global.room.base.AutoPullUpCountdownRoom;
import business.global.room.base.*;
import business.global.room.pk.PockerRoom;
import business.global.shareroom.ShareRoomMgr;
import business.nn.c2s.cclass.NNRoomSetInfo;
import business.nn.c2s.cclass.NN_define;
import business.nn.c2s.iclass.*;
import business.player.feature.PlayerRoom;
import cenum.*;
import cenum.room.GameRoomConfigEnum;
import cenum.room.RoomState;
import com.ddm.server.common.CommLogD;
import com.ddm.server.common.Config;
import com.ddm.server.websocket.def.ErrorCode;
import com.google.gson.Gson;

import business.global.room.RoomRecordMgr;
import core.network.http.proto.SData_Result;
import jsproto.c2s.cclass.BaseSendMsg;
import jsproto.c2s.cclass.pk.PKRoom_Record;
import jsproto.c2s.cclass.pk.PKRoom_RecordPosInfo;
import jsproto.c2s.cclass.room.BaseResults;
import jsproto.c2s.cclass.room.BaseRoomConfigure;
import business.nn.c2s.cclass.NN_define.NN_GameType;
import jsproto.c2s.cclass.room.GetRoomInfo;
import jsproto.c2s.cclass.room.RoomPosInfo;
import jsproto.c2s.iclass.S_GetRoomInfo;
import jsproto.c2s.iclass.room.SBase_Dissolve;
import jsproto.c2s.iclass.room.SBase_PosLeave;

public class NNRoom extends PockerRoom implements AutoPullUpCountdownRoom {
    public CNN_CreateRoom roomCfg; // 创建配置
    public final int maxCardCount = 5;
    // public final int endPointList[] = { 1, 2, 3, 4 };// {3, 4, 5, 10}; //阳阳 底分
    public final int endPointList[] = {1,2,3, 4, 5, 10};
    public final double guodiPercentList[] = { 0.025 };
    public final int backerPointList[] = { 0, 100, 150, 200 };
    private int callbacker = -1; // 庄家备份 -1:标识没有庄家
    boolean startNewGuo = true;
    private NNConfigMgr configMgr = new NNConfigMgr();
    public boolean isEnd = false;
    /**
     * 倒计时截止时间，玩家应在其前补足积分。
     */
    private long autoPullUpDeadlineMs = 0L;
    /**
     * 记录当前仍需补分的玩家 PID，用于日志展示与消息打点。
     */
    private final Set<Long> autoPullUpLackPidSet = new HashSet<>();

    protected NNRoom(BaseRoomConfigure<CNN_CreateRoom> baseRoomConfigure, String roomKey, long ownerID) {
        super(baseRoomConfigure, roomKey, ownerID);
        initShareBaseCreateRoom(CNN_CreateRoom.class, baseRoomConfigure);
        this.roomCfg = (CNN_CreateRoom) baseRoomConfigure.getBaseCreateRoom();
    }

    /**
     * 房间内每个位置信息 管理器
     */
    @Override
    public AbsRoomPosMgr initRoomPosMgr() {
        return new NNRoomPosMgr(this);
    }

    @Override
    public void initCreatePos() {
        if (this.getOwnerID() <= 0L) {
            return;
        }
        // 进入房间
        this.enterRoomVisitor(this.getOwnerID(), this.getRoomPosMgr().getPlayerNum(), false, null);
    }

    /**
     * 获取房间配置
     *
     * @return
     */
    public CNN_CreateRoom getRoomCfg() {
        if (this.roomCfg == null) {
            initShareBaseCreateRoom(CNN_CreateRoom.class, getBaseRoomConfigure());
            return (CNN_CreateRoom) getBaseRoomConfigure().getBaseCreateRoom();
        }
        return this.roomCfg;
    }

    @SuppressWarnings("unchecked")
    @Override
    public <T> T getCfg() {
        return (T) getRoomCfg();
    }

    @Override
    public String dataJsonCfg() {
        // 获取房间配置
        return new Gson().toJson(this.getRoomCfg());
    }

    /**
     * 清除记录。
     */
    @Override
    public void clearEndRoom() {
        super.clear();
        this.roomCfg = null;
    }

    @Override
    public boolean getCurSetUpdate(int sec) {
        return getCurSet().update(sec);
    }

    @Override
    public void startNewSet() {
        this.setCurSetID(this.getCurSetID() + 1);
        this.createSet();
        // 每个位置，清空准备状态
        this.getRoomPosMgr().clearGameReady();
    }

    // 创建set
    public void createSet() {
        NNRoomSet lastSet = (NNRoomSet) this.getCurSet();
        Map<Integer, Integer> winControlMap = new HashMap<>();
        if (null != this.getCurSet()) {
            winControlMap = ((NNRoomSet) this.getCurSet()).winControlMap;
            this.getCurSet().clear();
            this.setCurSet(null);
        }
        if (PrizeType.RoomCard == this.getBaseRoomConfigure().getPrizeType()) {
            if (roomCfg.getSign() == NN_GameType.NN_ZYQZ.value()) {
                this.setCurSet(new NNRoomSet_ZYQZ(this, winControlMap));
            } else if (this.roomCfg.getSign() == NN_GameType.NN_MPQZ.value()) {
                this.setCurSet(new NNRoomSet_MPQZ(this, winControlMap));
            } else if (this.roomCfg.getSign() == NN_GameType.NN_NNSZ.value()) {
                this.setCurSet(new NNRoomSet_NNSZ(this, winControlMap));
            } else if (this.roomCfg.getSign() == NN_GameType.NN_GDZJ.value()) {
                this.setCurSet(new NNRoomSet_GDZJ(this, winControlMap));
            } else if (this.roomCfg.getSign() == NN_GameType.NN_TBNN.value()) {
                this.setCurSet(new NNRoomSet_TBNN(this, winControlMap));
            } else if (NN_GameType.NN_LZNN.value() == this.roomCfg.getSign()) {
                this.setCurSet(new NNRoomSet_LZNN(this, winControlMap));
            } else if (NN_GameType.NN_DGN.value() == this.roomCfg.getSign()) {
                this.setCurSet(new NNRoomSet_DGN(this, this.roomCfg.guodi, (NNRoomSet_DGN) lastSet, winControlMap));
            } else {
                isEnd = true;// 2023-07-31 yaoms 参数有误 会导致房间无限重开，直接结束房间
            }
        }
        this.getRoomTyepImpl().roomSetIDChange();
    }

    @Override
    public void setEndRoom() {
        if (null != this.getCurSet()) {
            if (getHistorySet().size() > 0) {
                // 增加房局记录
                RoomRecordMgr.getInstance().add(this);
                ((NNRoomSet) this.getCurSet()).getRoomPlayBack()
                        .playBack2All(SNN_RoomEnd.make(this.getPKRoomRecordInfo()));
                refererReceiveList();
            }
        }
    }

    /**
     * 继续房间相关信息
     */
    @Override
    protected void continueRoom() {
        this.getRoomPosMgr().getPosList().forEach(x -> {
            if (x.isRobot()) {
                // if(Math.random() > 0.5){
                x.getPlayer().getFeature(PlayerRoom.class).continueFindAndEnter();// PosInfoEnum.LAST_PLAYER_INDEX.value()
                // }
            }
        });
    }

    /*
     * 主动离开房间的其他条件 条件不满足不退出
     */
    @Override
    public boolean exitRoomOtherCondition(long pid) {
        // 游戏已经开始，不能自由离开 防止一人加入一人退出时bug
        if (this.getCurSet() != null && this.getCurSet().getSetID() == 0
                && !RoomState.Init.equals(this.getRoomState())) {
            return false;
        }
        // 玩家玩过游戏就不能离开
        NNRoomPos pos = (NNRoomPos) this.getRoomPosMgr().getPosByPid(pid);
        return pos == null || !pos.isPlayTheGame();
    }

    /**
     * 构建房间回放返回给客户端
     *
     * @return 通知结构体
     */
    public PKRoom_Record getPKRoomRecordInfo() {
        PKRoom_Record pkRoom_record = new PKRoom_Record();
        pkRoom_record.setCnt = this.getHistorySetSize();
        pkRoom_record.recordPosInfosList = this.getRecordPosInfoList();
        pkRoom_record.roomID = this.getRoomID();
        pkRoom_record.endSec = this.getGameRoomBO().getEndTime();
        return pkRoom_record;
    }

    @Override
    public void calcEnd() {
        if (PrizeType.RoomCard == this.getBaseRoomConfigure().getPrizeType()
                && this.roomCfg.getSign() == NN_GameType.NN_GDZJ.value() && this.roomCfg.shangzhuangfenshu > 0) {
            int sourcePoint = this.getRoomPosMgr().getPosByPosID(0).getPoint();
            this.getRoomPosMgr().getPosByPosID(0)
                    .setPoint(sourcePoint - this.backerPointList[this.roomCfg.shangzhuangfenshu]);
        }
        super.calcEnd();
    }

    // 获取最大玩家数
    public int getMaxPlayerNum() {
        return ShareDefine.MAXPLAYERNUM_NN;
    }

    // 获取庄家
    public int getCallBacker() {
        return this.callbacker;
    }

    // 设置庄家
    public void setCallBacker(int callBacker) {
        this.callbacker = callBacker;
    }

    @Override
    protected List<PKRoom_RecordPosInfo> getRecordPosInfoList() {
        List<PKRoom_RecordPosInfo> list = new ArrayList<>();
        for (BaseResults x : this.allRecordPosInfoList.values()) {
            list.add((PKRoom_RecordPosInfo) x);
        }
        return list;
    }

    /**
     * 加入房间的其他条件 条件不满足不进入
     */
    @Override
    public boolean enterRoomOtherCondition(long pid) {
        if (this.getRoomCfg().gaojixuanxiang.contains(0) && this.getRoomState() != RoomState.Init) {
            return false;
        }
        return true;
    }

    @Override
    public boolean autoStartGame() {
        // 亲友圈 大联盟2-8人自动开始游戏
        // zhaozw 如果满几人开始，如果已经有对应的人数了 返回 true，如果是手动开始返回false
        // if (RoomTypeEnum.checkUnionOrClub(this.getRoomTyepImpl().getRoomTypeEnum()))
        // {
        // return true;
        // }

        if (roomCfg != null && this.getRoomPosMgr() != null && roomCfg.youxikaishi >= 2 && roomCfg.youxikaishi <= 5) {
            int[] nums = { 2, 4, 5, this.getMaxPlayerNum() };
            return this.getRoomPosMgr().getPlayer().size() >= nums[roomCfg.youxikaishi - 2];
        }
        return false;
    }

    /**
     *
     * @param pid 用户ID
     */
    @Override
    public SData_Result startGame(long pid) {
        try {
            lock();
            if (this.getOwnerID() <= 0L) {
                setOwnerID(pid);
            }
            if (this.getOwnerID() <= 0L) {
                // 没有房主
                return SData_Result.make(ErrorCode.NotAllow, "ownerID <= 0L");
            }
            if (!RoomState.Init.equals(this.getRoomState())) {
                // 房间不是初始状态
                return SData_Result.make(ErrorCode.NotAllow, "RoomState not Init {%s}", this.getRoomState().name());
            }
            if (!startGameOtherCondition(pid)) {
                // 有玩家没准备
                return SData_Result.make(ErrorCode.NotAllow, "startGame not isAllready");
            }
            // 通知新房间创建
            this.getRoomTyepImpl().createNewSetRoom();
            // 进入游戏阶段
            this.setRoomState(RoomState.Playing);
            // 游戏开始操作
            this.startGameBase();
            // 共享更新
            if (Config.isShare()) {
                ShareRoomMgr.getInstance().addShareRoom(this);
            }
            return SData_Result.make(ErrorCode.Success);
        } finally {
            unlock();
        }
    }

    // 当前几个人在玩
    public int getPlayingCount() {
        int count = 0;
        if (this.getCurSet() != null) {
            count = ((NNRoomSet) this.getCurSet()).getPlayingCount();
        } else {
            count = this.getMaxPlayerNum();
        }
        return count;
    }

    @Override
    public DissolveRoom initDissolveRoom(int posID, int WaitSec) {
        return new NNDissolveRoom(this, posID, WaitSec);
    }

    @Override
    public void roomTrusteeship(int pos) {
        ((NNRoomSet) this.getCurSet()).roomTrusteeship(pos);
    }

    @Override
    public void RobotDeal(int pos) {
        ((NNRoomSet) this.getCurSet()).roomTrusteeship(pos);
    }

    /**
     * @return configMgr
     */
    public NNConfigMgr getConfigMgr() {
        return configMgr;
    }

    @Override
    public void cancelTrusteeship(AbsRoomPos pos) {
        ((NNRoomSet) this.getCurSet()).roomTrusteeship(pos.getPosID());
    }

    @Override
    public boolean isGodCard() {
        // TODO 自动生成的方法存根
        return this.getConfigMgr().isGodCard();
    }

    @Override
    public BaseSendMsg XiPai(long roomID, long pid, ClassType cType) {
        return SNN_XiPai.make(roomID, pid, cType);
    }

    @Override
    public BaseSendMsg ChatMessage(long pid, String name, String content, ChatType type, long toCId, int quickID) {
        return SNN_ChatMessage.make(pid, name, content, type, toCId, quickID);
    }

    @Override
    public boolean isCanChangePlayerNum() {
        return this.getBaseRoomConfigure().getBaseCreateRoom().getFangjian()
                .contains(GameRoomConfigEnum.FangJianQieHuanRenShu.ordinal());
    }

    @Override
    public BaseSendMsg ChangePlayerNum(long roomID, int createPos, int endSec, int playerNum) {
        return SNN_ChangePlayerNum.make(roomID, createPos, endSec, playerNum);
    }

    @Override
    public BaseSendMsg ChangePlayerNumAgree(long roomID, int pos, boolean agreeChange) {
        return SNN_ChangePlayerNumAgree.make(roomID, pos, agreeChange);
    }

    @Override
    public BaseSendMsg ChangeRoomNum(long roomID, String roomKey, int createType) {
        return SNN_ChangeRoomNum.make(roomID, roomKey, createType);
    }

    @Override
    public GetRoomInfo getRoomInfo(long pid) {
        S_GetRoomInfo ret = new S_GetRoomInfo();
        // 设置房间公共信息
        this.getBaseRoomInfo(ret);
        if (Objects.nonNull(this.getCurSet())) {
            ret.setSet(this.getCurSet().getNotify_set(pid));
        } else {
            ret.setSet(new NNRoomSetInfo());
        }
        return ret;
    }

    /**
     * 神牌消息
     *
     * @param msg
     * @param pid
     */
    @Override
    public boolean godCardMsg(String msg, long pid) {
        return false;
    }

    @Override
    public BaseSendMsg Trusteeship(long roomID, long pid, int pos, boolean trusteeship) {
        return SNN_Trusteeship.make(roomID, pid, pos, trusteeship);
    }

    @Override
    public BaseSendMsg PosLeave(SBase_PosLeave posLeave) {
        return SNN_PosLeave.make(posLeave);
    }

    @Override
    public BaseSendMsg LostConnect(long roomID, long pid, boolean isLostConnect, boolean isShowLeave) {
        return SNN_LostConnect.make(roomID, pid, isLostConnect, isShowLeave);
    }

    @Override
    public BaseSendMsg PosContinueGame(long roomID, int pos) {
        return SNN_PosContinueGame.make(roomID, pos);
    }

    @Override
    public BaseSendMsg PosUpdate(long roomID, int pos, RoomPosInfo posInfo, int custom) {
        return SNN_PosUpdate.make(roomID, pos, -1, posInfo, null, custom);
    }

    @Override
    // public BaseSendMsg PosUpdate(long roomID, int pos, int visit, RoomPosInfo posInfo, RoomPosInfo visitInfo, int custom) {
    public BaseSendMsg PosUpdate(long roomID, int pos, RoomPosInfo posInfo, int visit, RoomPosInfo visitInfo, int custom) {
        return SNN_PosUpdate.make(roomID, pos, visit, posInfo, visitInfo, custom);
    }

    @Override
    public BaseSendMsg VisitUpdate(long roomID, int action, int pos, RoomPosInfo posInfo, int visit,
            RoomPosInfo visitInfo) {
        return SNN_VisitUpdate.make(roomID, action, pos, posInfo, visit, visitInfo);
    }

    @Override
    public BaseSendMsg PosReadyChg(long roomID, int pos, boolean isReady) {
        return SNN_PosReadyChg.make(roomID, pos, isReady);
    }

    @Override
    public BaseSendMsg Dissolve(SBase_Dissolve dissolve) {
        return SNN_Dissolve.make(dissolve);
    }

    @Override
    public BaseSendMsg StartVoteDissolve(long roomID, int createPos, int endSec) {
        return SNN_StartVoteDissolve.make(roomID, createPos, endSec);
    }

    @Override
    public BaseSendMsg PosDealVote(long roomID, int pos, boolean agreeDissolve, int endSec) {
        return SNN_PosDealVote.make(roomID, pos, agreeDissolve);
    }

    @Override
    public BaseSendMsg Voice(long roomID, int pos, String url) {
        return SNN_Voice.make(roomID, pos, url);
    }

    @Override
    public <T> BaseSendMsg RoomRecord(List<T> records) {
        return SNN_RoomRecord.make(records);
    }

    @Override
    public int getTimerTime() {
        return 200;
    }

    /**
     * 房主是否需要准备
     *
     * @return
     */
    @Override
    public boolean ownerNeedReady() {
        return true;
    }

    /**
     * 自动准备游戏 玩家加入房间时，自动进行准备。 zhaozw
     */
    @Override
    public boolean autoReadyGame() {
        return getRoomCfg().getKexuanwanfa().contains(NN_define.KeXuanWanFa.ZiDong.getType());
    }

    /**
     * 倒数
     */
    private int daoNum = 1;

    public int getDaoNum() {
        return daoNum;
    }

    public void addDaoNum(int daoNum) {
        this.daoNum += daoNum;
    }

    @Override
    public boolean isEnd() {
        // zhaozw TODO 斗公牛特殊处理
        if (getBaseRoomConfigure().getBaseCreateRoom().getPlayerNum() == 6) {
            return this.getDaoNum() >= 7;
        } else if (getBaseRoomConfigure().getBaseCreateRoom().getPlayerNum() == 8) {
            return this.getDaoNum() >= 9;
        } else if (getBaseRoomConfigure().getBaseCreateRoom().getPlayerNum() == 10) {
            return this.getDaoNum() >= 11;
        }
        return isEnd;
    }

    @Override
    public boolean isOnlyWinRightNowPoint() {
        return true;
    }

    @Override
    public boolean isRulesOfCanNotBelowZero() {
        return true;
    }

    @Override
    public boolean checkAutoPullUp() {
        return checkAutoPullUpCountdown();
    }

    /**
     * 计算倒计时长度，优先使用房间配置的上分等待时间，默认 60 秒。
     */
    private long getAutoPullUpWaitMillis() {
        int waitSec = this.upwardTimeValue();
        if (waitSec <= 0) {
            waitSec = 60;
        }

        CommLogD.info("NNRoom autoPullUp wait time -> RoomID:{} 倒计时:{}秒", this.getRoomID(), waitSec);
        return waitSec * 1000L;
    }

    /**
     * 清空倒计时状态，表示所有玩家均已补足或无需等待。
     */
    private void resetAutoPullUpState() {
        if (!autoPullUpLackPidSet.isEmpty() || autoPullUpDeadlineMs > 0L) {
            CommLogD.info("NNRoom autoPullUp reset -> RoomID:{} 原倒计时清除, 缺分玩家:{}", this.getRoomID(), new ArrayList<>(autoPullUpLackPidSet));
        }
        autoPullUpDeadlineMs = 0L;
        autoPullUpLackPidSet.clear();
        this.getRoomPosMgr().notify2All(SNN_SportsPointNotEnough.make(this.getRoomID(), 0L, Collections.emptyList()));
    }

    @Override
    /**
     * 是否仍处于等待玩家补分的倒计时阶段。
     */
    public boolean isAutoPullUpCountdownActive() {
        return autoPullUpDeadlineMs > System.currentTimeMillis() && !autoPullUpLackPidSet.isEmpty();
    }

    @Override
    /**
     * 获取剩余等待秒数，倒计时结束后需要重新检查积分。
     */
    public long getAutoPullUpRemainSeconds() {
        if (!isAutoPullUpCountdownActive()) {
            return 0L;
        }
        long remainMs = autoPullUpDeadlineMs - System.currentTimeMillis();
        return Math.max(0L, remainMs / 1000L);
    }

    @Override
    /**
     * 当前欠分玩家的快照，便于日志与外部展示。
     */
    public Set<Long> getAutoPullUpLackPidSnapshot() {
        return new HashSet<>(autoPullUpLackPidSet);
    }

    @Override
    /**
     * 牛牛专用的自动拉起检查：首次欠分启动倒计时，到期仍未补足则触发解散。
     */
    public boolean checkAutoPullUpCountdown() {
        Double autoDismiss = this.getBaseRoomConfigure().getBaseCreateRoom().getAutoDismiss();
        double threshold = autoDismiss != null ? autoDismiss : 0D;
        List<AbsRoomPos> posList = this.getRoomPosMgr().getPosList().stream()
                .filter(Objects::nonNull)
                .filter(pos -> pos.getPid() > 0)
                .collect(Collectors.toList());

        List<Long> lackPidList = posList.stream()
                .filter(pos -> pos.isAutoDismiss(threshold))
                .map(AbsRoomPos::getPid)
                .collect(Collectors.toList());

        long now = System.currentTimeMillis();

        if (lackPidList.isEmpty()) {
            resetAutoPullUpState();
            return false;
        }

        if (autoPullUpDeadlineMs <= 0L || autoPullUpLackPidSet.isEmpty()) {
        long waitMillis = getAutoPullUpWaitMillis();
            autoPullUpDeadlineMs = now + waitMillis;
            autoPullUpLackPidSet.clear();
            autoPullUpLackPidSet.addAll(lackPidList);
        long remainSeconds = Math.max(1L, (waitMillis + 999L) / 1000L);
            CommLogD.warn("NNRoom autoPullUp start -> RoomID:{} 缺分玩家:{} 倒计时:{}秒", this.getRoomID(), lackPidList,
                    remainSeconds);
            this.getRoomPosMgr().notify2All(SNN_SportsPointNotEnough.make(this.getRoomID(), remainSeconds, lackPidList));
            return false;
        }

        autoPullUpLackPidSet.clear();
        autoPullUpLackPidSet.addAll(lackPidList);

        if (now < autoPullUpDeadlineMs) {
        long remain = Math.max(0L, (autoPullUpDeadlineMs - now + 999L) / 1000L);
            CommLogD.info("NNRoom autoPullUp waiting -> RoomID:{} 剩余{}秒, 缺分玩家:{}", this.getRoomID(), remain,
                    lackPidList);
        this.getRoomPosMgr().notify2All(SNN_SportsPointNotEnough.make(this.getRoomID(), remain, lackPidList));
            return false;
        }

        CommLogD.warn("NNRoom autoPullUp timeout -> RoomID:{} 缺分玩家:{} 超时仍未补足, 触发解散", this.getRoomID(),
                lackPidList);
        resetAutoPullUpState();
        return true;
    }
}
